/* dbupdate.c
 *
 * Copyright 2007-2008 OpenMoko, Inc.
 * Authored by Tick <tick@openmoko.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <dbupdate.h>
#include <pkutil.h>
#include <assassin.h>
#include <gears.h>
#include <browser.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <debug.h>


extern Ecore_Evas *ee;

static void pk_refresh_finished_cb (void *data, DBusMessage *message);
static void pk_refresh_update_progress_cb (void *data, DBusMessage *message);
static void pk_refresh_error_cb (void *data, DBusMessage *message);
static int network_check_failed_message(void *);
static void shutdown_installer_cb (void *data,  Evas_Object *obj, const char *emission, const char *source);

/**
 * @brief Check if the package database need to be updated or not. Here using time stamp to do this job.
 * @return 0 means no update needed, else is the package database need to be renew
 */
int  db_update_check() {
    put_a_dim_on_screen();
    int ret = network_check();
    if (ret == 0) {
        debug("Network check error. Need to connect first.\n");
        get_categories_page();
        ecore_timer_add(10, &network_check_failed_message, NULL);
        return 1;
    } else if ( ret < 0 ) { // DBus/OpkgLock error
        message_alert("Cannot initial Installer. Please make sure there are only one processing using libopk, then restart Installer.", NORMAL_ERROR, &shutdown_installer_cb);
        return 1;
    }
    if (db_update_timestamp_check()) {
        package_database_update();
        return 1;
    }
    return 0;
}

int db_update_timestamp_check() {
    char file[MAX_PACKAGE_NAME_SIZE];
    char buf[MAX_PACKAGE_NAME_SIZE];
    struct stat s;
    int time_now, time_last;
    FILE *fp;

    snprintf(file, sizeof(file), "%s/%s", THEME_PATH, STAMPFILE);
    time_now = time(NULL);
    if (stat(file, &s) < 0) {
        debug("State file failed!!\n");
        fp = fopen(file, "w");
        if (!fp) {
            error("Open timestamp file %s failed!!\n", file);
            return 0;
        }
        fclose(fp);
        return 1;  // Do refresh for the first time
    }
    fp = fopen(file, "r");
    if (!fp) {
        error("Open timestamp file %s failed!!\n", file);
        return 0;
    }
    fread(buf, sizeof(char), sizeof(buf), fp);
    fclose(fp);
    time_last = atoi(buf);
    debug("The timestamp is %d, now =%d\n", time_last, time_now);
    if ((time_now - time_last) > TIMEOUT_S) {
        return 1;
    }
    return 0;
}

void put_time_stamp() {
    int time_now;
    FILE *fp;
    char buf[MAX_PACKAGE_NAME_SIZE];
    char file[MAX_PACKAGE_NAME_SIZE];
    time_now = time(NULL);
    snprintf(buf, sizeof(buf), "%d", time_now);
    snprintf(file, sizeof(file), "%s/%s", THEME_PATH, STAMPFILE);
    fp = fopen(file, "w");
    if (!fp) {
        error("Open timestamp file %s failed!!\n", file);
    }
    fwrite(buf, sizeof(char), strlen(buf), fp);
    fclose(fp);
}



int network_check() {
    return pk_repos_ping();
}

static int __is_datebase_updating__ = 0;
static struct Installer_Progressbar * __db_refresh_progress_bar__ = NULL;
void package_database_update() {
    Pk_Operation *pko;
    __is_datebase_updating__ = 1;
    pko = pko_new();

    pko->Operations[Finished_sig] =         &pk_refresh_finished_cb;
    pko->Operations[ProgressChanged_sig] =  &pk_refresh_update_progress_cb;
    pko->Operations[ErrorCode_sig] = 	&pk_refresh_error_cb;

    pk_refresh_system(pko);
    __db_refresh_progress_bar__ = new_Progressbar(ecore_evas_get(ee), "");
    progressbar_text_set(__db_refresh_progress_bar__, "Refreshing ");
    progressbar_animator_start(__db_refresh_progress_bar__);
    put_a_mask_on_screen();
}


static void pk_refresh_finished_cb (void *data, DBusMessage *message) {
    char *exit_text;
    Pk_Operation * pko = (Pk_Operation *)data;
    char *error = pko->payload;
    unsigned int runtime;
    DBusError _err;

    pko_timeout_check(pko);

    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &exit_text,
                               DBUS_TYPE_UINT32,   &runtime,
                               DBUS_TYPE_INVALID )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);

    remove_the_mask_on_screen();

    if (__db_refresh_progress_bar__) {
        progressbar_animator_stop(__db_refresh_progress_bar__);
        free_Progressbar(__db_refresh_progress_bar__);
        __db_refresh_progress_bar__ = NULL;
    }
    pk_default_get_finished_cb (data, message);
    if (error) {
        network_check_failed_message(NULL);
        free(error);
    } else {
        put_time_stamp();
        get_categories_page();
    }
    //refresh_categories_lists();
    __is_datebase_updating__ = 0;
}


static void pk_refresh_update_progress_cb (void *data, DBusMessage *message) {
    Pk_Operation * pko = (Pk_Operation *)data;
    DBusError _err;
    unsigned int percentage, subpercentage, elapsed, remaining;
    debug("get progress cb!!\n");

    pko_timeout_check(pko);

    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_UINT32, &percentage,
                               DBUS_TYPE_UINT32, &subpercentage,
                               DBUS_TYPE_UINT32, &elapsed,
                               DBUS_TYPE_UINT32, &remaining,
                               DBUS_TYPE_INVALID)) {
        error("Error Message: %s\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Trans:%s Progress: %d %d %d %d\n", pko->tid, percentage, subpercentage, elapsed, remaining);
    if (__db_refresh_progress_bar__ && percentage < __db_refresh_progress_bar__->MAX) {
        progressbar_animator_stop(__db_refresh_progress_bar__);
        progressbar_update(__db_refresh_progress_bar__, percentage);
    }
}

static void pk_refresh_error_cb (void *data, DBusMessage *message) {
    char * error_code;
    char *detail;
    Pk_Operation * pko = (Pk_Operation *)data;
    DBusError _err;
    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &error_code,
                               DBUS_TYPE_STRING, &detail,
                               DBUS_TYPE_INVALID
                              )) {
        error("Error Message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Trans: %s error_code is '%s' '%s'\n", pko->tid, error_code, detail);
    if (pko->payload) free(pko->payload);
    pko->payload = strdup(error_code);
}


int package_database_updating() {
    return __is_datebase_updating__;
}



static int network_check_failed_message(void *date) {
    message_alert("<b><font_size=24>Cannot access repository. <br>Please check your network.</></b>", NORMAL_ERROR, NULL);
    return 0;
}

static void shutdown_installer_cb (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    exit(EXIT_SUCCESS);
}
